home *** CD-ROM | disk | FTP | other *** search
- /*
- MeasureMTF.c
- Copyright © 1990-1993 Denis G. Pelli
- Measures the modulation transfer function of a monitor. Uses drifting gratings,
- both horizontal and vertical, of constant temporal frequency. The results
- are saved in a KaleidaGraph text file, MTF?.data, where ? stands for the screen
- number. The data file includes the dc and second harmonic.
- HISTORY:
- 10/11/90 dgp wrote it.
- 10/12/90 dgp added dc and harmonics.
- 3/18/91 dgp updated to work with new PlotXY, but not tested
- 4/15/91 dgp Check for NewPaletteManager().
- 8/24/91 dgp Made compatible with THINK C 5.0.
- 8/27/92 dgp replace SysEnvirons() by Gestalt()
- 10/23/92 dgp try to read latest LuminanceRecord
- 2/7/93 dgp updated to use SetPixelsQuickly.
- */
- #include "VideoToolbox.h"
- #include "Luminance.h"
- //#include <Packages.h>
- #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
- #include <profile.h> /* for timing */
- #define SYMANTEC_C_PROFILE 1
- #endif
- #define TWO_PI (2.0*PI)
- #define MAX(a,b) ((a)>(b)?(a):(b))
- #define MIN(a,b) ((a)<(b)?(a):(b))
-
- void MeasureMTF(void);
- double MeasureContrast(GDHandle device,LuminanceRecord *LP,int tPeriod,double c[10],WindowPtr plotWindow);
- double saw(double x);
-
- void main(void)
- {
- assert(StackSpace()>1000);
- StackGrow(30000);
- Require(gestalt8BitQD);
- MeasureMTF();
- }
-
- void MeasureMTF(void)
- {
- GDHandle device,oldGDHandle;
- CWindowPtr window;
- WindowPtr plotWindow,mtfWindow;
- LuminanceRecord LR;
- char string[100],string2[100],outName[100];
- unsigned long seconds;
- FILE *outfile[2];
- int i,tPeriod,width;
- Rect r,srcRect,dstRect;
- double a,b,f,fNominal,c,L,cOut,cH[10],cV[10];
- PlotXYStyle mtfStyle[2];
- unsigned long row[1048]; // hopefully long enough for any video device
- short oldScreen;
-
- assert(StackSpace()>4000);
- MaximizeConsoleHeight();
- #if (THINK_C || THINK_CPLUS || SYMANTEC_C)
- console_options.ncols=100;
- printf("\n"); /* Initialize QuickDraw */
- #else
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitCursor();
- #endif
- #if SYMANTEC_C_PROFILE
- InitProfile(200,1);
- _profile=0; /* disable profiling for the moment */
- #endif
- printf("Welcome to MeasureMTF.\n");
- #include "LuminanceRecord1.h" // read at compile time
- oldScreen=LR.screen;
- if(GetScreenDevice(1)!=NULL){
- printf("Which screen would you like to calibrate (%d):",LR.screen);
- gets(string);
- sscanf(string,"%d",&LR.screen);
- }
- else LR.screen=0;
- sprintf(string,"LuminanceRecord%d.h",LR.screen);
- i=ReadLuminanceRecord(string,&LR,0); // try to read latest LuminanceRecord
- if(i<1)printf("Warning: couldn't find “%s”. Calibrating screen %d.\n"
- ,string,LR.screen);
- else oldScreen=LR.screen;
-
- mtfStyle[0].continuing=0;
- mtfStyle[0].lineWidth=1;
- mtfStyle[0].symbolWidth=0;
- mtfStyle[0].dash[0]=0;
- mtfStyle[0].dashOffset=0;
- mtfStyle[0].color=blackColor;
- mtfStyle[1]=mtfStyle[0];
- mtfStyle[1].color=blueColor;
-
- GetDateTime(&seconds);
- sprintf(outName,"MTF%d.%s",LR.screen,DatedString(seconds));
- outfile[0]=stdout;
- outfile[1]=fopen(outName,"w");
- if(outfile[1]==NULL) PrintfExit("Sorry, can't create \"%s\".\007\n",outName);
- SetFileInfo(outName,'TEXT','QKPT'); /* for Kaleidagraph */
-
- /* Find device corresponding to the experimental screen. */
- oldGDHandle=GetGDevice();
- device = GetScreenDevice(LR.screen);
- if(NewPaletteManager())
- SetDepth(device,8,1,1); /* 8-bit pixels, color mode */
- window = GDOpenWindow(device);
-
- if(oldScreen==LR.screen){
- printf("Luminance was calibrated on %s\n",LR.date);
- printf("at %.2f %s by %s\n",LR.LBackground,LR.units,LR.notes);
- printf("Range is %.1f to %.1f %s.\n",LR.LMin,LR.LMax,LR.units);
- }
- L=LR.LBackground; /* desired mean luminance */
- printf("Enter desired display luminance in cd/m^2 (%.2f):",L);
- fgets(string,sizeof(string)-1,stdin);
- sscanf(string,"%lf", &L);
-
- c=0.9;
- c=MIN(c,MIN((LR.LMax-L)/L,(L-LR.LMin)/L));
- printf("Contrast %.1f\n",c);
- printf("Computing sinusoidal lookup table . . .\n");
- for(i=0;i<256;i++){
- SetLuminance(device,&LR,i,L*(1.0+c*sin(TWO_PI*i/256.0)),L*(1.0-c),L*(1.0+c));
- }
-
- tPeriod=64; /* frames in one temporal period */
- width=window->portRect.right; /* pixels across screen */
- SetRect(&r,0,0,2*tPeriod,2*tPeriod);
- OffsetRect(&r,64,64);
- plotWindow=NewWindow(NULL,&r,"\pL",1,noGrowDocProc,(WindowPtr) -1L,FALSE,0L);
- SetRect(&r,0,0,192,192);
- OffsetRect(&r,640-r.right-64,64);
- mtfWindow=NewWindow(NULL,&r,"\pMTF",1,noGrowDocProc,(WindowPtr) -1L,FALSE,0L);
- MeasureContrast(device,&LR,tPeriod,cH,plotWindow); /* measure vBlack */
- ffprintf(outfile,
- "notes" "\tcycles/pixel" "\tcycles/screen"
- "\tHoriz. Grating Gain" "\tVert. Grating Gain" "\tVert./Horiz.");
- fprintf(outfile[0],"\n");
- fprintf(outfile[1],"\tc" "\tcH[0]" "\tcH[1]" "\tcH[2]" "\tcV[0]" "\tcV[1]" "\tcV[2]"
- "\t(cV[0]-cH[0])/cH[0]" "\tcV[2]/(c*V/H)^2" "\t(cV[0]-cH[0])/cH[0]/(c*V/H)^2"
- "\n");
- ffprintf(outfile,"notes"); /* put some text in notes column */
- for(fNominal=0.0;;fNominal=MAX(fNominal*pow(2.0,1.0/2.0),fNominal+1.0/width)){
- f=floor(fNominal*width+0.5)/width; /* integral periods for zero mean over line */
- if(fNominal>0.5)f=0.5;
- /* horizontal grating */
- srcRect=dstRect=window->portRect;
- dstRect.left=srcRect.right=1;
- for(i=0;i<srcRect.bottom;i++){
- row[0]=128.0+127.5*saw(TWO_PI*f*i);
- SetWindowPixelsQuickly((WindowPtr)window,0,i,row,1);
- }
- /* CopyBits uses the inverse color table of the current GDevice. */
- SetGDevice(device);
- CopyBits((BitMap *)*window->portPixMap,(BitMap *)*window->portPixMap,
- &srcRect,&dstRect,srcCopy,NULL);
- SetGDevice(oldGDHandle);
- cOut=MeasureContrast(device,&LR,tPeriod,cH,plotWindow);
- ffprintf(outfile,"\t%10.5f" "\t%10.2f" "\t%12.6f",f,f*dstRect.right,cOut/c);
- /* draw the MTF */
- PlotXY(mtfWindow
- ,log(f/(1./640.))/log(0.5/(1./640.)), log(cOut/c/0.3)/log(1.1/0.3)
- ,&mtfStyle[0]);
-
- /* vertical grating */
- srcRect=dstRect=window->portRect;
- dstRect.bottom=srcRect.top=srcRect.bottom-1;
- for(i=0;i<srcRect.right;i++)row[i]=128.0+127.5*saw(TWO_PI*f*i);
- SetWindowPixelsQuickly((WindowPtr)window,0,srcRect.top,row,srcRect.right);
- SetGDevice(device);
- CopyBits((BitMap *)*window->portPixMap,(BitMap *)*window->portPixMap,
- &srcRect,&dstRect,srcCopy,NULL);
- SetGDevice(oldGDHandle);
- cOut=MeasureContrast(device,&LR,tPeriod,cV,plotWindow);
- ffprintf(outfile,"\t%20.6f" "\t%18.6f",cOut/c,cV[1]/cH[1]);
- fprintf(outfile[0],"\n");
- fprintf(outfile[1],"\t%f" "\t%f" "\t%f" "\t%f" "\t%f" "\t%f" "\t%f"
- ,c,cH[0],cH[1],cH[2],cV[0],cV[1],cV[2]);
- a=(cV[0]-cH[0])/cH[0]; /* dc error, expressed as a contrast */
- b=cV[1]/cH[1]; /* contrast gain of video amplifier */
- fprintf(outfile[1],"\t%f" "\t%f" "\t%f" "\n"
- ,a,cV[2]/(c*b*c*b),a/(c*b*c*b));
- /* draw the MTF */
- PlotXY(mtfWindow
- ,log(f/(1./640.))/log(0.5/(1./640.)), log(cOut/c/0.3)/log(1.1/0.3)
- ,&mtfStyle[1]);
-
- if(f==0.5)break;
- }
-
- /* print notes */
- IUDateString(seconds,longDate,(unsigned char *)string);
- p2cstr((unsigned char *)string);
- IUTimeString(seconds,FALSE,(unsigned char *)string2);
- p2cstr((unsigned char *)string2);
- ffprintf(outfile,"%s %s\n",string2,string);
- ffprintf(outfile,"%.1f Hz\n",1.0/(tPeriod*0.015));
- ffprintf(outfile,"%.2f cd/m^2\n",L);
- ffprintf(outfile,"%.3f contrast\n",c);
- ffprint saw(double x)
- /* returns sawtooth function with same phase, amplitude, and symmetry as sin() */
- {
- x/=TWO_PI;
- x-=0.5;
- x-=floor(x);
- return 2.0*x-1.0;
- }
-